Spring Security

Spring security介绍

1.认证(authentication)与授权(access-control)

基本的权限控制流程

  1. 用户使用账号密码登录
  2. 校验验证码与密码
  3. 获取用户信息
  4. 为用户创建权限存储对象
  5. 后续访问时的权限控制

前三步为认证,后两步为授权

认证

  1. 通过AbstractAuthenticationProcessingFilter过滤器将账号密码组装成Authentication实现类UsernamePasswordAuthenticationToken;
  2. 将token传递给AuthenticationManager验证是否有效,而AuthenticationManager通常使用ProviderManager实现类来检验;
  3. AuthenticationManager认证成功后将返回一个拥有详细信息的Authentication object(包括权限信息,身份信息,细节信息,但密码通常会被移除);
  4. 通过SecurityContextHolder.getContext().getAuthentication().getPrincipal()将Authentication设置到security context中;

授权

  1. 通过FilterSecurityInterceptor过滤器入口进入;
  2. FilterSecurityInterceptor通过其继承的抽象类的AbstractSecurityInterceptor.beforeInvocation(Object,object)方法进行访问授权,其中涉及了类AuthenticationManager、AccessDecisionManager、SecurityMetadataSource等。

2.Spring Security核心组件

  • SecurityContextHolder

    ​提供对SecurityContext的访问,存储security context(用户信息、角色权限等),有一个名为principal的Object对象,这个Object默认为UserDetail对象,也可以自定义用户对象。

  • Authentication

    描述当前用户的相关信息,其包含用户拥有的权限信息列表、用户细节信息。通常为UsernamePasswordAuthenticationToken

  • UserDetails&GrantedAuthority

    UserDetails提供从数据源构建Authentication对象所需的信息,包含GrantedAuthority。

    UserDetails与Authentication接口功能类似,其实含义即是Authentication为用户提交的认证凭证(账号密码),UserDetails为系统中用户正确认证凭证,在UserDetailsService中的loadUserByUsername方法获取正确的认证凭证。

3.Spring Security核心服务类

  • AuthenticationManager、ProviderManager以及AuthenticationProvider

    AuthenticationManager是认证相关的核心接口,是认证一切的起点。但常见的认证流程都是AuthenticationManager实现类ProviderManager处理,而且ProviderManager实现类基于委托者模式维护AuthenticationProvider 列表用于不同的认证方式。

    ProviderManager中的AuthenticationProvider列表,会依照次序去认证,默认策略下,只需要通过一个AuthenticationProvider的认证,即可被认为是登录成功,而且AuthenticationProvider认证成功后返回一个Authentication实体,并为了安全会进行清除密码。如果所有认证器都无法认证成功,ProviderManager会抛出一个ProviderNotFoundException异常。

  • UserDetailsService

    UserDetailsService接口作用是从特定的地方获取认证的数据源(账号、密码)。如何获取到系统中正确的认证凭证,通过loadUserByUsername(String username)获取认证信息

  • AccessDecisionManager&SecurityMetadataSource

    由AbstractSecurityInterceptor调用,负责做出最终的访问控制决策。

  • PasswordEncoder

    密码加密接口,实现类有使用BCrypt hash算法实现的BCryptPasswordEncoder,SCrypt hashing 算法实现的SCryptPasswordEncoder实现类

4.Spring Security核心过滤器

  • FilterSecurityInterceptor

    该类根据访问的用户的角色,权限授权访问那些资源

  • UsernamePasswordAuthenticationFilter

    最常用的过滤器,根据请求中包含的账号、密码,组建UsernamePasswordAuthenticationToken的Authentication实体

    认证时,调用AuthenticationManager进行认证,根据认证结果返回成功或者失败

    只有在使用formLogin方法之后才会有

  • AnonymousAuthenticationFilter

    它位于常用的身份认证过滤器(如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter)之后,意味着只有在上述身份过滤器执行完毕后,SecurityContext依旧没有用户信息,AnonymousAuthenticationFilter该过滤器才会有意义——基于用户一个匿名身份。

  • SecurityContextPersistenceFilter

    SecurityContextPersistenceFilter的两个主要作用便是request来临时,创建SecurityContext安全上下文信息和request结束时清空SecurityContextHolder。

5.Spring Security认证流程

![Spring Security认证流程图](E:\sunline\微服务源码\一些记录\Spring security认证流程.png)

Spring security配置

1.WebSecurityConfigurer

Spring security config具有三个模块,一共有3个builder,认证相关的AuthenticationManagerBuilder和web相关的WebSecurity、HttpSecurity。
  • AuthenticationManagerBuilder:用来配置全局的认证相关的信息,其实就是AuthenticationProvider和UserDetailsService,前者是认证服务提供商,后者是用户详情查询服务;
  • WebSecurity: 全局请求忽略规则配置(比如说静态文件,比如说注册页面)、全局HttpFirewall配置、是否debug配置、全局SecurityFilterChain配置、privilegeEvaluator、expressionHandler、securityInterceptor;
  • HttpSecurity:具体的权限控制规则配置。一个这个配置相当于xml配置中的一个标签。各种具体的认证机制的相关配置,OpenIDLoginConfigurer、AnonymousConfigurer、FormLoginConfigurer、HttpBasicConfigurer等。

webSecurity和httpSecurity的关系

在filterChainProxy中,维护着每种请求对应的filter拦截链,每次会遍历一下这个列表,找到不同url请求对应的拦截链。如果在webSecurity中配置了ignoring,则没有对应的拦截链。那么就会调用application注册的拦截器,包括Springboot自带的拦截器与@component注册的拦截器。不同url对应的拦截器则由httpSecurity进行配置与注册。

filterChainProxy中,webSecurity是一个基本的拦截链,httpSecurity是其中的一部分。当httpSecurity没有配置对应的拦截器时,则使用webSecurity注册的拦截器。
WebSecurityConfigurerAdapter提供了简洁方式来创建WebSecurityConfigurer,其作为基类,可通过实现该类自定义配置类,主要重写这三个方法:
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
public void configure(WebSecurity web) throws Exception {}
protected void configure(HttpSecurity httpSecurity) throws Exception {}
从WebSecurityConfigurerAdapter源码init初始化模块中的“获取HttpSecurity”和“配置FilterSecurityInterceptor拦截器到WebSecurity”中可以看出,想要spring Security如何知道我们要求所有用户都经过身份验证? Spring Security如何知道我们想要支持基于表单的身份验证?只要重写protected void configure(HttpSecurity http) throws Exception方法即可。

2.filter执行顺序

Filter class说明
ChannelProcessingFilter访问协议控制过滤器,可能会将我们重新定向到另外一种协议,从http转换成https
SecurityContextPersistenceFilter创建SecurityContext安全上下文信息和request结束时清空SecurityContextHolder
ConcurrentSessionFilter并发访问控制过滤器,主要功能:SessionRegistry中获取SessionInformation来判断session是否过期,从而实现并发访问控制。
HeaderWriterFilter给http response添加一些Header
CsrfFilter跨域过滤器,跨站请求伪造保护Filter
LogoutFilter处理退出登录的Filter
X509AuthenticationFilter添加X509预授权处理机制支持
CasAuthenticationFilter认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行
UsernamePasswordAuthenticationFilter认证的filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行。表单认证是最常用的一个认证方式。
BasicAuthenticationFilter认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行
SecurityContextHolderAwareRequestFilter此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API
JaasApiIntegrationFilter(JAAS)认证方式filter
RememberMeAuthenticationFilter记忆认证处理过滤器,即是如果前面认证过滤器没有对当前的请求进行处理,启用了RememberMe功能,会从cookie中解析出用户,并进行认证处理,之后在SecurityContextHolder中存入一个Authentication对象。
AnonymousAuthenticationFilter匿名认证处理过滤器,当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中
SessionManagementFilter会话管理Filter,持久化用户登录信息,可以保存到session中,也可以保存到cookie或者redis中
ExceptionTranslationFilter异常处理过滤器,主要拦截后续过滤器(FilterSecurityInterceptor)操作中抛出的异常。
FilterSecurityInterceptor安全拦截过滤器类,获取当前请求url对应的ConfigAttribute,并调用accessDecisionManager进行访问授权决策。

PCMC中的应用

1.认证实现

登录前

  • VerityCodeFilter:验证码认证器
  • LoginPerFilter:单点登录,nologin链接登录时的认证器

登录验证

  • UsernamePasswordAuthenticationFilter:spring security的账号密码认证器,只有在登录url时生效,验证输入的账号密码是否正确

登录后

  • JwtAuthorizationTokenFilter:校验输入的token里的用户,去redis里拿对应的保存的登陆用户
  • RestRequestFilter:Rest请求的认证器,认证其渠道信息是否正确

认证流程图

2.授权实现

功能点权限

FunctionPermissionFilter:对当前用户的角色的功能点权限进行校验,判断当前url是否能访问

流程图

数据权限

  • DataPermissionInterceptor:数据权限的mybatis拦截器,会在mybatis的sql执行前进行拦截。
流程:
  1. 拦截器拿到查询SQL的本体PlainSelect以及入参paramHashMap
  2. 将入参中permissionEntity的权限规则转换成表达式的集合88
    1. 遍历入参paramHashMap的键值对,获取permissionEntity权限实体类
    2. 根据实体类中的角色编号、请求Url(或功能编号)、请求参数获取对应的权限规则与规则明细集合
    3. 遍历这些权限规则,将对应的规则明细转换成对应的表达式类
      1. 遍历每个权限规则明细,放入当前用户编号
      2. 根据规则明细中的操作符,调用对应的方法进行解析,每个操作符对应一个操作类
      3. 解析规则明细中的字段名,作为操作类的左边表达式
      4. 根据规则明细中的规则类型,调用对应的类解析值,作为操作类的右边表达式
    4. 将第3步的表达式与规则明细中的连接符,组装成表达式类
  3. 将表达式的集合拼接进PlainSelect的where条件中,转换成String重新返回到batis的属性

参考资料